﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using AdminModule.IndexMarkServiceReference;
using System.Windows.Data;
using System.Reflection;
using System.Windows.Controls.Primitives;
using AdminModule.CommonClasses;
using System.Collections;

namespace AdminModule.Views
{
    public enum EnumSelectObjects { AdmCharacteristic, AdmDepartment, AdmGradation, AdmSection,
        AdmPerson, AdmGradationsStruct, AdmGradationsStructSelectGroup, AdmResponsible };

    public enum TemplateType { Hierarchical, Lines };

    public partial class SelectAddItemsWindow : ChildWindow, IAddObjectSelector
    {
        private IndexMarkServiceClient client = new IndexMarkServiceClient();

        private EnumSelectObjects selectObjects;
        private int IDTerm;
        private int max_include_level = int.MaxValue;

        // Коллекция существующих элементов (потомки элемента, в который будет осуществляться добавление элементов)
        private IEnumerable<DTOBase> existsItems = new List<DTOBase>();
        // Коллекция выбранных элементов (если это CheckBox)
        private List<DTOBase> list = new List<DTOBase>();
        // Выбранный объект (если это RadaioButton)
        private DTOBase obj = null;

        // Флаг, который сообщает о том, что уже существующие в объекте дочерние элементы необходимо исключить из списка выбора
        private bool? _includeItems;
        private bool? includeItems
        {
            get
            {
                return this._includeItems;
            }
            set
            {
                if (this._includeItems == null)
                    this._includeItems = value;
            }
        }

        private bool? _isMultipleSelection = null;
        private bool? isMultipleSelection
        {
            get
            {
                return this._isMultipleSelection;
            }
            set
            {
                if (this._isMultipleSelection == null)
                this._isMultipleSelection = value;
            }
        }

        #region ItemsSource
        /// <summary>
        /// Осущевствляем двустороннюю привязку коллекции к элементу управления
        /// </summary>
        /// <param name="control">Элемент управления</param>
        /// <param name="collection">Коллекция элементов</param>
        private void SetItems(ItemsControl control, System.Collections.IEnumerable collection)
        {
            if (includeItems == true)
                itemsSource = CheckItems(null, collection, existsItems, 1);
            else
            {
                if(includeItems != null)
                    itemsSource = ExcludeItems(collection, existsItems);
            }
            Binding binding = new Binding("ItemsSource") { Source = this, Mode = BindingMode.TwoWay };
            // Устанавливаем привязку к переданной коллекции
            control.SetBinding(ItemsControl.ItemsSourceProperty, binding);
        }

        private IEnumerable<DTOBase> itemsSource = new List<DTOBase>();
        public IEnumerable<DTOBase> ItemsSource
        {
            get { return this.itemsSource; }
        }
        #endregion

        /// <summary>
        /// Рекурсивно обходит коллекции объектов, помечает уже существующие дочерние элементы и добавлется их в коллекцию выбранных объектов (если это необходимо)
        /// </summary>
        /// <param name="queryResult"></param>
        /// <param name="existsItems"></param>
        /// <returns></returns>
        private IEnumerable<DTOBase> CheckItems(DTOBase parentQueryResult, IEnumerable queryResult, IEnumerable<DTOBase> existsItems, int level)
        {
            IEnumerable<DTOBase> _queryResult = queryResult.Cast<DTOBase>();
            foreach (DTOBase el in existsItems)
            {
                // Сравниваем объекты по ключу и типу
                DTOBase tmp = _queryResult.FirstOrDefault(f => f.GetKey() == el.GetKey() && f.GetObjType() == el.GetObjType());
                if (tmp != null)
                {
                    tmp.Checked = true;
                    tmp.Enabled = true;
                    if (isMultipleSelection == true && max_include_level >= level)
                    {
                        list.Add(tmp);
                    }
                    if (isMultipleSelection == false && level == 1) this.obj = tmp;
                }
                if (tmp is DTOChilds && el is DTOChilds)
                    CheckItems((tmp as DTOChilds), (tmp as DTOChilds).Childs, (el as DTOChilds).Childs, level + 1);
            }
            foreach (DTOBase el in _queryResult)
            {
                DTOBase tmp = existsItems.FirstOrDefault(f => f.GetKey() == el.GetKey() && f.GetObjType() == el.GetObjType());
                if (tmp == null && (parentQueryResult != null && parentQueryResult.Checked != true && parentQueryResult.Enabled != true))
                {
                    el.Checked = false;
                    el.Enabled = false;
                }
                else
                {
                    el.Enabled = true;
                }
            }
            return _queryResult;
        }

        /// <summary>
        /// Обходит данные, пришедшие от сервера, и удаляет дочерние объекты, которые уже существуют у родительского объекта.
        /// Не является рекурсивной. Обходит только корневые элементы и не затрагивает коллекции Childs
        /// </summary>
        /// <param name="queryResult"></param>
        /// <param name="existsItems"></param>
        /// <returns></returns>
        private IEnumerable<DTOBase> ExcludeItems(IEnumerable queryResult, IEnumerable<DTOBase> existsItems)
        {
            List<DTOBase> _queryResult = new List<DTOBase>(queryResult.Cast<DTOBase>());
            foreach (DTOBase el in existsItems)
            {
                DTOBase tmp = _queryResult.FirstOrDefault(f => f.GetKey() == el.GetKey());
                if (tmp != null) _queryResult.Remove(tmp);
            }
            return _queryResult;
        }

        #region Конструкторы
        public SelectAddItemsWindow(EnumSelectObjects selectObjects, bool? isMultipleSelection = null)
        {
            InitializeComponent();

            this.selectObjects = selectObjects;
            this.isMultipleSelection = isMultipleSelection;

            IEnumerable l = null;
            _tFilter.TextChanged += (s_, e_) =>
            {
                var s = _tFilter.Text;
                if (l == null) l = listBox.ItemsSource;
                listBox.ItemsSource = l.Cast<object>().Where(x =>
                    x.ToString().Contains(s));
            };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="selectObjects"></param>
        /// <param name="IDTerm"></param>
        /// <param name="isMultipleSelection"></param>
        /// <param name="includeItems"></param>
        /// <param name="max_include_level">Максимальный уроверь иерархии, который может попасть в коллекцию отмеченных элементов.
        /// Используется только в методе CheckItems в момент ицициализации формы. Узлы будут отмечены голочками, 
        /// но не будут добавлены в коллекцию выбранных элементов.
        /// </param>
        public SelectAddItemsWindow(EnumSelectObjects selectObjects, int IDTerm, bool? isMultipleSelection = null, bool? includeItems = null, int max_include_level = int.MaxValue)
        {
            InitializeComponent();

            this.selectObjects = selectObjects;
            this.IDTerm = IDTerm;
            this.isMultipleSelection = isMultipleSelection;
            this.includeItems = includeItems;
            this.max_include_level = max_include_level;
        }
        #endregion

        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            // ИСПОЛЬЗОВАЛОСЬ КОГДА В КОРНЕ ПОКАЗАТЕЛЯ МОГЛА БЫТЬ ТОЛЬКО ОДНА ГРУППА
            //if (selectObjects == EnumSelectObjects.AdmGradationsStruct)
            //{
            //    AdmChildsBase tmp = treeView.Items.Cast<AdmChildsBase>().FirstOrDefault(w => w.Checked == true);
            //    if (tmp != null)
            //    {
            //        this.obj = GetSelectedTree(tmp);
            //    }
            //}
            if (this.obj == null && this.list.Count == 0)
            {
                MessageBox.Show("Выберите объекты из списка", MyAssemblyInfo.Title, MessageBoxButton.OK);
                return;
            }

            this.DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }

        public DTOBase GetObject()
        {
            return this.obj;
        }

        public IEnumerable<DTOBase> GetObjects()
        {
            return this.list;
        }

        private void ChildWindow_Loaded(object sender, RoutedEventArgs e)
        {
            progressBar.IsVisibility = Visibility.Visible;

            switch (this.selectObjects)
            {
                case EnumSelectObjects.AdmCharacteristic:
                    SetTemplate("AdmCharacteristic");
                    client.GetAdmCharacteristicsCompleted += (s, ev) =>
                        {
                            SetItems(listBox, ev.Result);
                            progressBar.IsVisibility = Visibility.Collapsed;
                        };
                    client.GetAdmCharacteristicsAsync();
                    break;
                case EnumSelectObjects.AdmDepartment:
                    SetTemplate("AdmDepartment");
                    client.GetAdmDepartmentsCompleted += (s, ev) =>
                        {
                            SetItems(listBox, ev.Result);
                            progressBar.IsVisibility = Visibility.Collapsed;
                        };
                    client.GetAdmDepartmentsAsync(AdmDepartmentSortMode.FullName);
                    break;
                case EnumSelectObjects.AdmGradation:
                    SetTemplate("AdmGradation");
                    client.GetAdmGradationsCompleted += (s, ev) =>
                    {
                        SetItems(listBox, ev.Result);
                        progressBar.IsVisibility = Visibility.Collapsed;
                    };
                    client.GetAdmGradationsAsync();
                    break;
                case EnumSelectObjects.AdmSection:
                    SetTemplate("AdmSection");
                    client.GetAdmSectionsCompleted += (s, ev) =>
                        {
                            SetItems(listBox, ev.Result);
                            progressBar.IsVisibility = Visibility.Collapsed;
                        };
                    client.GetAdmSectionsAsync();
                    break;
                case EnumSelectObjects.AdmPerson:
                    SetTemplate("AdmPerson");
                    client.GetAdmPersonsCompleted += (s, ev) =>
                        {
                            SetItems(listBox, ev.Result);
                            progressBar.IsVisibility = Visibility.Collapsed;
                        };
                    client.GetAdmPersonsAsync();
                    break;

                case EnumSelectObjects.AdmGradationsStruct:
                    SetTemplate("AdmGradationsStruct", TemplateType.Hierarchical);
                    client.GetGradationsStructCompleted += (s, ev) =>
                        {
                            SetItems(treeView, ev.Result);
                            progressBar.IsVisibility = Visibility.Collapsed;
                        };
                    client.GetGradationsStructAsync(IDTerm);
                    break;

                case EnumSelectObjects.AdmGradationsStructSelectGroup:
                    SetTemplate("AdmGradationsStructSelectGroup", TemplateType.Hierarchical);
                    client.GetGradationsStructCompleted += (s, ev) =>
                    {
                        SetItems(treeView, ev.Result);
                        progressBar.IsVisibility = Visibility.Collapsed;
                    };
                    client.GetGradationsStructAsync(IDTerm);
                    break;

                case EnumSelectObjects.AdmResponsible:
                    SetTemplate("AdmResponsible");
                    client.GetAdmResponsiblesCompleted += (s, ev) =>
                        {
                            SetItems(listBox, ev.Result);
                            progressBar.IsVisibility = Visibility.Collapsed;
                        };
                    client.GetAdmResponsiblesAsync(IDTerm);
                    break;
            }
        }

        private void SetTemplate(string templateName, TemplateType templateType = TemplateType.Lines)
        {
            switch (templateType)
            {
                case TemplateType.Lines:
                    listBox.ItemTemplate = this.Resources[templateName] as DataTemplate;
                    listBox.Visibility = System.Windows.Visibility.Visible;
                    break;
                case TemplateType.Hierarchical:
                    treeView.ItemTemplate = this.Resources[templateName] as HierarchicalDataTemplate;
                    treeView.Visibility = System.Windows.Visibility.Visible;
                    break;
            }
        }

        public void ShowForm(IEnumerable<DTOBase> existsItems, bool isMultipleSelection, bool includeItems)
        {
            this.existsItems = existsItems;
            this.isMultipleSelection = isMultipleSelection;
            this.includeItems = includeItems;
            Show();
        }

        public void ShowForm(IEnumerable<DTOBase> existsItems, bool includeItems)
        {
            this.existsItems = existsItems;
            this.includeItems = includeItems;
            Show();
        }

        private void Button_Loaded(object sender, RoutedEventArgs e)
        {
            ToggleButton tButton = sender as ToggleButton;
            if (sender is RadioButton)
            {
                tButton.Visibility = (isMultipleSelection == false) ? Visibility.Visible : Visibility.Collapsed;
            }
            if (sender is CheckBox)
            {
                tButton.Visibility = (isMultipleSelection == true) ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        private void radioButton_Checked(object sender, RoutedEventArgs e)
        {
            this.obj = (sender as RadioButton).Tag as DTOBase;
        }

        private void checkBox_Checked(object sender, RoutedEventArgs e)
        {
            DTOBase checkedObject = (sender as CheckBox).Tag as DTOBase;
            if(!this.list.Contains(checkedObject)) this.list.Add(checkedObject);
        }

        private void checkBox_Unchecked(object sender, RoutedEventArgs e)
        {
            this.list.Remove((sender as CheckBox).Tag as DTOBase);
        }

        #region Выбор элементов в дереве
        /// <summary>
        /// Рекурсивно обходит дочерние объекты и делает их отмеченными
        /// </summary>
        /// <param name="obj"></param>
        private void RecursiveCheckItems(DTOChilds obj)
        {
            // Если передан null значит объект не является производным от AdmBaseClass
            if (obj == null) return;
            foreach(DTOBase el in obj.Childs)
            {
                el.Checked = true;
                el.Enabled = true;
                RecursiveCheckItems(el as DTOChilds);
            }
        }

        /// <summary>
        /// Рекурсивно обходит дочерние объекты и делает их НЕ отмеченными
        /// </summary>
        /// <param name="obj"></param>
        private void RecursiveUncheckItems(DTOChilds obj)
        {
            // Если передан null значит объект не является производным от AdmBaseClass
            if (obj == null) return;
            foreach (DTOBase el in obj.Childs)
            {
                el.Checked = false;
                el.Enabled = false;
                RecursiveUncheckItems(el as DTOChilds);
            }
        }

        //private void radioButtonHierarchical_Checked(object sender, RoutedEventArgs e)
        //{
        //    RadioButton rb = sender as RadioButton;
        //    AdmChildsBase obj = rb.Tag as AdmChildsBase;
        //    this.obj = obj;
        //    RecursiveCheckItems(obj);
        //}

        //private void radioButtonHierarchical_Unchecked(object sender, RoutedEventArgs e)
        //{
        //    RadioButton rb = sender as RadioButton;
        //    AdmChildsBase obj = rb.Tag as AdmChildsBase;
        //    RecursiveUncheckItems(obj);
        //}

        //private void checkBoxHierarchical_Unchecked(object sender, RoutedEventArgs e)
        //{
        //    CheckBox cb = sender as CheckBox;
        //    AdmChildsBase obj = cb.Tag as AdmChildsBase;
        //    RecursiveUncheckItems(obj);
        //}

        //private void checkBoxHierarchical_Checked(object sender, RoutedEventArgs e)
        //{
        //    CheckBox cb = sender as CheckBox;
        //    AdmChildsBase obj = cb.Tag as AdmChildsBase;
        //    RecursiveCheckItems(obj);
        //}

        /// <summary>
        /// Рекурсивно обходит выбранное дерево объектов и исключает из него не отмеченные элементы
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        //private AdmBaseClass GetSelectedTree(AdmChildsBase obj)
        //{
        //    List<AdmBaseClass> objectsForDelete = new List<AdmBaseClass>();
        //    foreach (AdmBaseClass el in obj.Childs)
        //    {
        //        if (el.Checked == true)
        //        {
        //            if (el is AdmChildsBase) GetSelectedTree(el as AdmChildsBase);
        //        }
        //        else
        //        {
        //            objectsForDelete.Add(el);
        //        }
        //    }
        //    foreach (AdmBaseClass el in objectsForDelete) { obj.Childs.Remove(el); }
        //    return obj;
        //}
        #endregion

        private void radioButtonHierarchicalStructSelectGroup_Checked(object sender, RoutedEventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            this.obj = rb.Tag as DTOBase;
        }

        private void checkBoxHierarchical_Click(object sender, RoutedEventArgs e)
        {
            CheckBox cb = sender as CheckBox;
            DTOChilds obj = cb.Tag as DTOChilds;
            if (cb.IsChecked == true)
                RecursiveCheckItems(obj);
            else
                RecursiveUncheckItems(obj);
        }

        private void radioButtonHierarchical_Click(object sender, RoutedEventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            DTOChilds obj = rb.Tag as DTOChilds;
            if (rb.IsChecked == true)
            {
                RecursiveCheckItems(obj);
                this.obj = obj;
            }
            else
                RecursiveUncheckItems(obj);
        }

        private void checkBoxHierarchical_Click_1(object sender, RoutedEventArgs e)
        {
            CheckBox cb = sender as CheckBox;
            DTOChilds obj = cb.Tag as DTOChilds;
            if (cb.IsChecked == true)
            {
                RecursiveCheckItems(obj);
                this.checkBox_Checked(sender, e);
            }
            else
            {
                RecursiveUncheckItems(obj);
                this.checkBox_Unchecked(sender, e);
            }
        }
    }
}

